package tasks.io.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tasks.io.core.*;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;


public class ConnectionCompletionHandler implements CompletionHandler<Void, ConnectionCompletionHandlerVo> {
    private static final Logger log = LoggerFactory.getLogger(ConnectionCompletionHandler.class);


    @Override
    public void completed(Void result, ConnectionCompletionHandlerVo attachment) {
        handler(attachment, null, result);
    }


    @Override
    public void failed(Throwable throwable, ConnectionCompletionHandlerVo attachment) {
        handler(attachment, throwable, null);
    }


    private void handler(ConnectionCompletionHandlerVo connectionCompletionHandlerVo, Throwable throwable, Void result) {
        ClientChannelContext clientChannelContext = connectionCompletionHandlerVo.getClientChannelContext();
        AsynchronousSocketChannel asynchronousSocketChannel = connectionCompletionHandlerVo.getAsynchronousSocketChannel();
        Client client = connectionCompletionHandlerVo.getTioClient();
        TioClientConfig tioClientConfig = client.getTioClientConfig();
        Node serverNode = connectionCompletionHandlerVo.getServerNode();
        String bindIp = connectionCompletionHandlerVo.getBindIp();
        Integer bindPort = connectionCompletionHandlerVo.getBindPort();
        TioClientListener tioClientListener = tioClientConfig.getTioClientListener();
        boolean isReconnect = connectionCompletionHandlerVo.isReconnect();
        boolean isConnected = false;

        try {
            if (throwable == null) {
                if (isReconnect) {
                    clientChannelContext.setAsynchronousSocketChannel(asynchronousSocketChannel);
                    //				channelContext.getDecodeRunnable().setCanceled(false);
                    clientChannelContext.handlerRunnable.setCanceled(false);
                    //		channelContext.getHandlerRunnableHighPrior().setCanceled(false);
                    clientChannelContext.sendRunnable.setCanceled(false);
                    //		channelContext.getSendRunnableHighPrior().setCanceled(false);

                    tioClientConfig.closeds.remove(clientChannelContext);
                } else {
                    clientChannelContext = new ClientChannelContext(tioClientConfig, asynchronousSocketChannel);
                    clientChannelContext.setServerNode(serverNode);
                }

                clientChannelContext.setBindIp(bindIp);
                clientChannelContext.setBindPort(bindPort);

                clientChannelContext.getReconnCount().set(0);
                clientChannelContext.setClosed(false);
                isConnected = true;

                connectionCompletionHandlerVo.setClientChannelContext(clientChannelContext);

                //				tioClientConfig.ips.bind(channelContext);
                tioClientConfig.connecteds.add(clientChannelContext);

                ReadCompletionHandler readCompletionHandler = clientChannelContext.getReadCompletionHandler();
                ByteBuffer readByteBuffer = readCompletionHandler.getReadByteBuffer();//ByteBuffer.allocateDirect(channelContext.tioConfig.getReadBufferSize());
                readByteBuffer.position(0);
                readByteBuffer.limit(readByteBuffer.capacity());
                asynchronousSocketChannel.read(readByteBuffer, readByteBuffer, readCompletionHandler);

                log.info("connected to {}", serverNode);
                if (isConnected && !isReconnect) {
                    clientChannelContext.channelStat.setTimeFirstConnected(SystemTimer.currTime);
                }
            } else {
                log.error(throwable.toString(), throwable);
                if (clientChannelContext == null) {
                    ReconnConf reconnConf = tioClientConfig.getReconnConf();
                    if (reconnConf != null) {
                        clientChannelContext = new ClientChannelContext(tioClientConfig, asynchronousSocketChannel);
                        clientChannelContext.setServerNode(serverNode);
                    }
                }

                if (!isReconnect) //不是重连，则是第一次连接
                {
                    if (clientChannelContext != null) {
                        connectionCompletionHandlerVo.setClientChannelContext(clientChannelContext);
                    }
                }
                boolean f = ReconnConf.put(clientChannelContext);
                if (!f) {
                    Io.close(clientChannelContext, null, "不需要重连，关闭该连接", true, false, CloseCode.CLIENT_CONNECTION_FAIL);
                }
            }
        } catch (Throwable e) {
            log.error(e.toString(), e);
        } finally {
            if (connectionCompletionHandlerVo.getCountDownLatch() != null) {
                connectionCompletionHandlerVo.getCountDownLatch().countDown();
            }

            try {
                if (clientChannelContext != null) {
                    clientChannelContext.setReconnect(isReconnect);

                    {
                        if (tioClientListener != null) {
                            tioClientListener.onAfterConnected(clientChannelContext, isConnected, isReconnect);
                        }
                    }

                }
            } catch (Throwable e1) {
                log.error(e1.toString(), e1);
            }
        }
    }
}
